package it.antonio.ilpizze.db.core;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

import it.antonio.ilpizze.db.Backup;
import it.antonio.ilpizze.db.Backups;
import it.antonio.ilpizze.db.exceptions.DbInternalException;
import it.antonio.leaves.util.Files;

public class BackupManager implements Backups {

	private DbFileSystem fs;
	private StatusManager statusManager;
	private LockManager lockManager;
	
	public BackupManager(DbFileSystem fs, StatusManager statusManager, LockManager lockManager) {
		this.fs = fs;
		this.statusManager = statusManager;
		this.lockManager = lockManager;
	}

	@Override
	public void create(String name) {
		lockManager.checkMultiThreadingWrite();
		
		File backupDirectory = fs.backupDirectory();
		File[] files = fs.filesToBackup();
		try {
			File zip = new File(backupDirectory, name + ".zip");

			FileOutputStream fout = new FileOutputStream(zip);
			ZipOutputStream zout = new ZipOutputStream(fout);

			for (File file : files) {
				byte[] buffer = new byte[1024];

				FileInputStream fin = new FileInputStream(file);

				zout.putNextEntry(new ZipEntry(file.getName()));

				int length;

				while ((length = fin.read(buffer)) > 0) {
					zout.write(buffer, 0, length);
				}

				zout.closeEntry();

				fin.close();
			}

			zout.close();
			
		} catch (Exception e) {
			throw new DbInternalException(e);
		}

	}

	@Override
	public List<Backup> list() {
		lockManager.checkMultiThreadingRead();
		File backupDirectory = fs.backupDirectory();
		File[] files = backupDirectory.listFiles();

		List<Backup> backups = new ArrayList<Backup>();
		for (File file : files) {
			backups.add(new Backup(Files.getNameWithoutExtension(file), new Date(file.lastModified())));
		}
		return backups;
	}

	@Override
	public void restore(String name) {
		
		lockManager.checkMultiThreadingWrite();
		fs.closeAllFiles();
		
		File backupDirectory = fs.backupDirectory();
		File dbDirectory = fs.dbDirectory();
		File zip = new File(backupDirectory, name + ".zip");
		
		ZipInputStream zis = null;
		try {
			zis = new ZipInputStream(new FileInputStream(zip));
			ZipEntry ze = zis.getNextEntry();

			byte[] buffer = new byte[1024];
			while (ze != null) {

				String fileName = ze.getName();
				File newFile = new File(dbDirectory, fileName);

				FileOutputStream fos = new FileOutputStream(newFile);

				int len;
				while ((len = zis.read(buffer)) > 0) {
					fos.write(buffer, 0, len);
				}

				fos.close();
				ze = zis.getNextEntry();
			}
		} catch (Exception e) {
			throw new DbInternalException(e);
		} finally {
			
			try {
				if(zis != null) zis.close();
			} catch (IOException e) {
				throw new DbInternalException(e);
			}
			fs.unlockDb();
			statusManager.status(Status.NEED_RESTART);
		}
	}

	@Override
	public void restore(Backup backup) {
		restore(backup.name());
	}

}
